<template>
  <div class="app-container">
    <el-page-header @back="goBack" content="在线咨询"></el-page-header>
    <div class="app-container">
      <el-row>
        <transition name="el-zoom-in-top">
          <el-col :span="8">
            <el-card style="width: 100%; min-height: 500px; color: #333">
              <div style="padding-bottom: 10px; border-bottom: 1px solid #ccc">正在咨询</div>
              <div style="padding: 10px 0">
                <div class="chat-item">
                  <!-- 用户名 -->
                  <span>
                    <el-tag size="small" style="margin-right:5px;">姓名</el-tag>
                    {{ receiveUser.name }}
                    <el-tag size="small" style="margin-right:5px;">电话</el-tag>
                    {{ receiveUser.phone }}
                  </span>
                  <!-- 聊天中 -->
                  <span style="font-size: 12px;color: limegreen; margin-left: 5px">chatting...</span>
                  <span v-if="noneVisitCount > 0">
                    <el-badge style="margin-top:5px;" :value="noneVisitCount" class="item"></el-badge>
                  </span>
                </div>
              </div>
            </el-card>
          </el-col>
        </transition>
        <transition name="el-zoom-in-top">
          <el-col :span="16" v-loading="historyLoading">
            <div
              style="width: 800px; margin: 0 auto; background-color: white;border-radius: 5px; box-shadow: 0 0 10px #ccc"
            >
              <div
                style="text-align: center; line-height: 50px;"
              >在线咨询（{{ receiveUser ? receiveUser.name : '暂未选择用户'}}）</div>
              <transition name="el-zoom-in-top">
                <div
                  ref="messageContainer"
                  style="height: 350px; overflow:auto; border-top: 1px solid #ccc"
                  v-html="content"
                  v-show="isShow"
                  @scroll="handleScroll"
                ></div>
              </transition>
              <div style="height: 200px">
                <textarea
                  v-model="text"
                  style="height: 160px; width: 100%; padding: 20px; border: none; border-top: 1px solid #ccc;border-bottom: 1px solid #ccc; outline: none"
                ></textarea>
                <div style="text-align: right; padding-right: 10px">
                  <el-button
                    type="info"
                    size="mini"
                    :loading="historyLoading"
                    @click="addHistory"
                  >查看历史</el-button>
                  <el-button
                    :loading="sendLoading"
                    type="primary"
                    size="mini"
                    @click="send"
                    v-hasPermi="['gonitis:chat:interflow']"
                  >发送</el-button>
                </div>
              </div>
            </div>
          </el-col>
        </transition>
      </el-row>
    </div>
  </div>
</template>
  
<script>
import { chatHistory } from "@/api/gonitis/chat";
export default {
  name: "interflow",

  computed: {
    sendId() {
      return this.$store.getters.userId;
    },
    sendAvatar() {
      return this.$store.getters.avatar;
    }
  },

  data() {
    return {
      // token参数
      // token: "Bearer " + getToken(),
      sendLoading: false, // 发送按钮加载
      historyLoading: false, // 获取历史加载
      noneVisitCount: 0,
      isShow: true,
      queryParams: {
        receiveId: undefined,
        firstQuery: undefined,
        pageNum: 1,
        pageSize: 10
      },
      loading: false,
      receiveUser: {
        id: null,
        name: null,
        phone: null,
        avatar: null
      },
      historyList: [],
      hasNext: false,
      defaultUrl:
        "https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png",
      text: "",
      content: "",
      websock: null, //建立的连接
      lockReconnect: false, //是否真正建立连接
      timeout: 30 * 1000, //30秒一次心跳
      reconnectInterval: 5 * 1000, // 5秒一次重连
      timeoutObj: null, //心跳倒计时
      timeoutnum: null //断开 重连倒计时
    };
  },
  created() {
    // 初始化
    this.init();
  },
  beforeDestroy() {
    // 关闭WebSocket连接
    if (this.websock) {
      this.websock.close();
      this.websocketclose();
      this.stop();
      this.websock.onclose = null;
      this.websock = null;
    }
  },
  methods: {
    /** 初始化 */
    init() {
      const id = this.$route.params.id;
      this.queryParams.firstQuery = 1;
      this.queryParams.receiveId = id;
      this.getInitList(id);
      this.initWebSocket();
    },
    /** 执行滚动到底部后的操作 */

    handleScroll() {
      const container = this.$refs.messageContainer;
      const isAtBottom =
        container.scrollHeight - container.clientHeight <=
        container.scrollTop + 5;
      if (isAtBottom) {
        this.noneVisitCount = 0;
      }
    },
    /** 在加载新消息后的适当时机调用 */

    scrollToBottom() {
      this.$nextTick(() => {
        const container = this.$refs.messageContainer;
        container.scrollTop = container.scrollHeight;
      });
    },
    /** 在加载历史消息完成后的适当时机调用 */

    scrollToTop() {
      this.$nextTick(() => {
        const container = this.$refs.messageContainer;
        container.scrollTop = 0;
      });
    },
    /** 获取历史消息 */

    async getInitList() {
      await this.getHistoryList();
      this.scrollToBottom();
    },
    /** 回退到发布页面 */

    goBack() {
      // 路由跳转到指定页面
      this.$store.dispatch("tagsView/delView", this.$route).then(() => {
        this.$router.push({
          path: "/gonitis/chat"
        });
      });
    },
    /** 设置发送者信息 */
    setReceiveUser(receiveUser) {
      this.receiveUser.name = receiveUser.name;
      this.receiveUser.id = receiveUser.id;
      this.receiveUser.phone = receiveUser.phone;
      this.receiveUser.avatar = receiveUser.avatar
        ? receiveUser.avatar
        : this.defaultUrl;
      this.queryParams.firstQuery = undefined;
    },
    /** 获取历史信息 */
    async getHistoryList() {
      this.isShow = false;
      this.historyLoading = true;
      this.loading = true;
      const query = this.queryParams;
      await chatHistory(query).then(response => {
        const list = response.data.chatHistory;
        if (this.queryParams.firstQuery === 1) {
          const receiveUser = response.data.receiveUser;
          this.setReceiveUser(receiveUser);
        }
        this.hasNext = list.length < this.queryParams.pageSize ? false : true;
        const newList = [...list.reverse(), ...this.historyList];
        this.historyList = newList;
        this.displayChat();
        this.loading = false;
        this.isShow = true;
        this.historyLoading = false;
      });
    },
    // 加载更多历史消息
    async addHistory() {
      // 判断是否还有下一页数据
      if (!this.hasNext) {
        this.$message({ type: "warning", message: "数据已经加载完毕" });
        return;
      }
      // 判断是否正在请求其它数据，如果是，则不发起额外的请求
      if (this.loading) return;
      this.queryParams.pageNum += 1; // 让页码值自增 +1
      await this.getHistoryList();
      this.scrollToTop(); // 跳转到页面顶部
    },
    /** 点击发送按钮触发事件 */

    send() {
      if (!this.text) {
        this.$message({
          message: "请先填写咨询内容",
          type: "warn",
          duration: 1500
        });
      }
      this.sendLoading = true; // 按钮加载动画
      let replyContent = {
        content: this.text,
        receiveId: this.receiveUser.id
      };
      // 获取发送时间
      const now = new Date();
      const time = `${now.getHours()}:${now.getMinutes()}`;
      // 往historyList添加当前消息
      const sendData = {
        identity: 1,
        msg: this.text,
        msgId: "",
        sendId: this.sendId,
        sendTime: time
      };
      this.historyList.push(sendData);
      this.websock.send(JSON.stringify(replyContent)); // 发送封装好的消息
      this.isShow = false;
      this.displayMyContent(time); // 在当前页面显示
      this.scrollToBottom(); // 跳转到底部
      this.isShow = true;
      this.sendLoading = false;
    },
    /** 发送消息后展示当前发送的消息到页面 */

    displayMyContent(time) {
      const avatar = this.sendAvatar ? this.sendAvatar : this.defaultUrl;
      let resCotent = this.getNowUserBubble(this.text, avatar, time);
      this.content += resCotent;
      this.text = "";
    },
    /** 初始化连接 */

    initWebSocket() {
      const sendId = this.sendId;
      const wsuri = process.env.VUE_APP_WEBSOCKET + "/webSocket/" + sendId;
      this.websock = new WebSocket(wsuri);
      this.websock.onopen = () => {
        this.websocketonopen();
      };

      this.websock.onerror = error => {
        this.websocketonerror(error);
      };

      this.websock.onmessage = event => {
        this.websocketonmessage(event);
      };

      this.websock.onclose = () => {
        this.websocketclose();
      };
    },

    websocketonerror(e) {
      //连接失败事件
      console.log("WebSocket连接发生错误");
      //重连
      this.reconnect();
    },

    // 开启心跳
    start() {
      this.stop(); // 先停止之前的心跳，避免重复启动
      // 启动心跳和重连定时器
      if (!this.timeoutObj) {
        this.timeoutObj = setInterval(() => {
          if (this.websock === null) {
            console.log("主动关闭");
            this.stop();
            return;
          }

          if (this.websock.readyState === WebSocket.OPEN) {
            // 如果连接正常
            this.timeoutnum && window.clearInterval(this.timeoutnum);
            this.timeoutnum = null;
            this.websock.send("$ping$");
          } else {
            // 否则重连
            this.isReconnect = true; // 修改当前的重连参数
            this.reconnect();
          }
        }, this.timeout);
      }
    },

    // 停止心跳()
    stop() {
      // 清空定时器
      window.clearInterval(this.timeoutObj);
      this.timeoutObj = null;
      window.clearInterval(this.timeoutnum);
      this.timeoutnum = null;
    },

    websocketonopen() {
      console.log("开启连接");
      //开启心跳
      this.start();
    },

    // 关闭连接
    websocketclose(e) {
      console.log("连接关闭");
    },

    /** 断线后重新连接 */

    reconnect() {
      console.log("断线重连");
      if (this.lockReconnect) {
        return;
      }
      this.lockReconnect = true; // 设置重连参数
      this.websock.close(); // 关闭旧连接

      // 延迟一定时间后进行重连
      this.timeoutnum = setTimeout(() => {
        // 新连接
        this.initWebSocket();
        this.lockReconnect = false;
      }, this.reconnectInterval);
    },
    /** 展示监听捕获当前信息 */

    websocketonmessage(event) {
      //打印收到服务器的内容
      const data = event.data;
      if (data === "$pong$") {
        this.start(); //收到服务器信息，心跳重置
        return;
      }
      const respData = JSON.parse(data);
      if (respData.code !== 200) {
        this.$message.error(respData.msg);
      } else {
        this.noneVisitCount += 1;
        const content = respData.data;
        const sendId = respData.sendId;
        // 获取发送时间
        const now = new Date();
        const time = `${now.getHours()}:${now.getMinutes()}`;
        // 往historyList添加当前消息
        const receiveData = {
          identity: 0,
          msg: content,
          msgId: "",
          sendId: sendId,
          sendTime: time
        };
        this.historyList.push(receiveData); // 保存到历史消息中
        this.displayContent(content, time);
      }
    },
    /** 展示收到的信息到页面上 */

    displayContent(text, time) {
      const avatar = this.receiveUser.avatar;
      const resCotent = this.getRemoteUserBubble(text, avatar, time);
      this.content += resCotent;
    },

    /** 展示聊天记录 */
    displayChat() {
      // 遍历historyList
      this.content = "";
      let list = this.historyList;
      for (let i = 0; i < list.length; i++) {
        if (list[i].identity == 1) {
          // 自己的气泡
          const avatar = this.sendAvatar ? this.sendAvatar : this.defaultUrl;
          const resCotent = this.getNowUserBubble(
            list[i].msg,
            avatar,
            list[i].sendTime
          );
          this.content += resCotent;
        } else {
          // 别人的气泡
          const avatar = this.receiveUser.avatar;
          const resCotent = this.getRemoteUserBubble(
            list[i].msg,
            avatar,
            list[i].sendTime
          );
          this.content += resCotent;
        }
      }
    },

    getNowUserBubble(text, avatar, time) {
      let html =
        '<div class="el-row" style="padding: 5px 0">\n' +
        '  <div class="el-col el-col-22" style="text-align: right; padding-right: 10px">\n' +
        '    <div class="tip left">' +
        text +
        "</div>\n" +
        '    <div class="time" style="color: #999; font-size: 12px; margin-top: 4px;">' +
        time +
        "</div>\n" +
        "  </div>\n" +
        '  <div class="el-col el-col-2">\n' +
        '    <span class="el-avatar el-avatar--circle" style="height: 40px; width: 40px; line-height: 40px;">\n' +
        `      <img src=${avatar} style="height: 40px; width: 40px; object-fit: cover;">\n` +
        "    </span>\n" +
        "  </div>\n" +
        "</div>";

      return html;
    },

    getRemoteUserBubble(text, avatar, time) {
      let html =
        '<div class="el-row" style="padding: 5px 0">\n' +
        '  <div class="el-col el-col-2" style="text-align: right">\n' +
        '    <span class="el-avatar el-avatar--circle" style="height: 40px; width: 40px; line-height: 40px;">\n' +
        `      <img src=${avatar} style="height: 40px; width: 40px; object-fit: cover;">\n` +
        "    </span>\n" +
        "  </div>\n" +
        '  <div class="el-col el-col-22" style="text-align: left; padding-left: 10px">\n' +
        '    <div class="tip right">' +
        text +
        "</div>\n" +
        '    <div class="time" style="color: #999; font-size: 12px; margin-top: 4px;">' +
        time +
        "</div>\n" +
        "  </div>\n" +
        "</div>";

      return html;
    }
  }
};
</script>
<style>
.tip {
  color: white;
  text-align: center;
  border-radius: 10px;
  font-family: sans-serif;
  padding: 10px;
  display: inline-block !important;
  display: inline;
}
.right {
  background-color: deepskyblue;
}
.left {
  background-color: forestgreen;
}
.el-icon-arrow-right {
  margin-left: 10px;
  font-size: 16px;
}

.chat-item {
  display: flex;
  border-radius: 0px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  padding: 10px 5px;
}

.app-header {
  margin-right: 5px;
  display: flex;
  align-items: center;
  cursor: pointer;
}
</style>